package cn.fenqing.utils;

import cn.fenqing.exception.ObjectUtilException;
import cn.fenqing.function.RunTime;
import lombok.Builder;
import lombok.Data;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author fenqing
 */
public class ObjectUtils {

    static abstract class Const{
        public static final Pattern PATTERN = Pattern.compile("(?<=\\[)(\\d+)(?=\\])");
        private static Method MAP_GET_METHOD;

        static {
            try {
                MAP_GET_METHOD = Map.class.getMethod("get", Object.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }

        public static Method getMapGetMethod() {
            return MAP_GET_METHOD;
        }
    }

    @Data
    @Builder
    public static class MatchMapper{
        private Collection<?> src;
        private String targetMapField;
        private String srcMapField;
        private String targetValueField;
        private String srcValueField;
    }

    /**
     * 获取对象里属性值
     * @param o     对象
     * @param field 字段名
     * @return      属性值
     */
    public static Object getValue(Object o, String field){
        if(o == null){
            return null;
        }
        Class<?> clazz = o.getClass();
        Matcher matcher = Const.PATTERN.matcher(field);
        if(matcher.find()){
            String group = matcher.group(0);
            int index = Integer.parseInt(group);
            if(o instanceof List){
                return ((List) o).get(index);
            }else{
                return Array.get(o, index);
            }
        }else{
            if(o instanceof Map){
                Method get = Const.getMapGetMethod();
                try {
                    return get.invoke(o, field);
                } catch (Exception e) {
                    throw new ObjectUtilException(e);
                }
            }else{
                try {
                    Field classField = clazz.getDeclaredField(field);
                    classField.setAccessible(true);
                    return classField.get(o);
                } catch (Exception e) {
                    Method getMethod = getGetMethod(clazz, field);
                    try {
                        return getMethod.invoke(o);
                    } catch (Exception ex) {
                        throw new ObjectUtilException(e);
                    }
                }
            }
        }
    }

    /**
     * 获取对象里属性值
     * @param o     对象
     * @param field 字段名
     * @return      属性值
     */
    public static boolean setValue(Object o, String field, Object value){
        if(o == null){
            return false;
        }
        Class<?> clazz = o.getClass();
        Matcher matcher = Const.PATTERN.matcher(field);
        if(matcher.find()){
            String group = matcher.group(0);
            int index = Integer.parseInt(group);
            if(o instanceof List){
                ((List) o).set(index, value);
                return true;
            }else{
                Array.set(o, index, value);
                return true;
            }
        }else{
            try {
                Field classField = clazz.getDeclaredField(field);
                classField.setAccessible(true);
                classField.set(o, value);
                return true;
            } catch (Exception e) {
                Method getMethod = getSetMethod(clazz, field, value.getClass());
                try {
                    getMethod.invoke(o, value);
                    return true;
                } catch (Exception ex) {
                    throw new ObjectUtilException(e);
                }
            }
        }
    }

    /**
     * 获取对象里属性值
     * @param o          对象
     * @param field      属性
     * @param returnType 返回类型
     * @param <T>        返回类型
     * @return           属性值
     */
    public static <T> T getValue(Object o, String field, Class<T> returnType){
        return (T) getValue(o, field);
    }

    /**
     * 获取对象里属性值
     * @param o     对象
     * @param field 字段名,支持级联
     * @return      属性值
     */
    public static Object getValueDeep(Object o, String field){
        String[] split = field.split("\\.");
        return getValueDeep(o, split, 0);
    }

    /**
     * 获取对象里属性值
     * @param o     对象
     * @param field 字段名,支持级联
     * @return      属性值
     */
    public static <T> T getValueDeep(Object o, String field, Class<T> returnType){
        return (T) getValueDeep(o, field);
    }

    /**
     * 获取对象里属性
     * @param collection
     * @param fields
     * @return
     */
    public static Map<String, List> getValues2List(Collection<?> collection, String... fields){
        return getValues2Collection(collection, ArrayList::new, fields);
    }

    /**
     * 匹配字段
     * @param collection
     * @param fields
     * @return
     */
    public static Map<String, Set> getValues2Set(Collection<?> collection, String... fields){
        return getValues2Collection(collection, HashSet::new, fields);
    }

    /**
     * 匹配字段
     * @param target
     * @param src
     * @param targetMapField
     * @param srcMapField
     * @param targetValueField
     */
    public static void match(Collection<?> target, Collection<?> src, String targetMapField, String srcMapField, String targetValueField){
        Map<Object, ?> map = src.stream().collect(Collectors.toMap(item -> getValue(item, srcMapField), e -> e));
        target.forEach(item -> setValue(item, targetValueField, map.get(getValue(item, targetMapField))));
    }

    /**
     * 匹配字段
     * @param target              目标集合
     * @param src                 数据来源
     * @param targetMapField      目标匹配字段
     * @param srcMapField         来源匹配字段
     * @param targetValueField    目标值字段
     * @param srcValueField       来源值字段
     */
    public static void match(Collection<?> target, Collection<?> src, String targetMapField, String srcMapField, String targetValueField, String srcValueField){
        Map<Object, ?> map = src.stream().collect(Collectors.toMap(item -> getValue(item, srcMapField), e -> e));
        target.forEach(item -> {
            Object o = map.get(getValue(item, targetMapField));
            if("this".equals(srcValueField)){
                setValue(item, targetValueField, o);
            }else{
                setValue(item, targetValueField, Optional.ofNullable(o).map(e -> getValue(e, srcValueField)).orElse(null));
            }
        });
    }

    /**
     * 匹配字段
     * @param target              目标集合
     * @param src                 数据来源
     * @param targetMapField      目标匹配字段
     * @param srcMapField         来源匹配字段
     * @param targetValueField    目标值字段
     * @param srcValueField       来源值字段
     */
    public static void match(Collection<?> target, Collection<?> src, String targetMapField, String srcMapField, String targetValueField, String srcValueField, Object defaultValue){
        Map<Object, ?> map = src.stream().collect(Collectors.toMap(item -> getValue(item, srcMapField), e -> e));
        target.forEach(item -> {
            Object o = map.get(getValue(item, targetMapField));
            if("this".equals(srcValueField)){
                setValue(item, targetValueField, Optional.ofNullable(o).orElse(defaultValue));
            }else{
                setValue(item, targetValueField, Optional.ofNullable(o).map(e -> getValue(e, srcValueField)).orElse(defaultValue));
            }
        });
    }

    public static void match(Collection<?> target, Collection<?> src, String targetMapField, String srcMapField, Map<String, String> valueMapper){
        Map<Object, ?> map = src.stream().collect(Collectors.toMap(item -> getValue(item, srcMapField), e -> e));
        target.forEach(item -> {
            valueMapper.forEach((targetValue, srcValue) -> {
                Object srcValueObject = map.get(getValue(item, targetMapField));
                if("this".equals(srcValue)){
                    setValue(item, targetValue, srcValueObject);
                }else{
                    setValue(item, targetValue, Optional.ofNullable(srcValueObject).map(e -> getValue(e, srcValue)).orElse(null));
                }
            });
        });
    }

    public static void match(Collection<?> target, Supplier<List<MatchMapper>> supplier){
        supplier.get().forEach(matchMapper -> match(target, matchMapper.getSrc(), matchMapper.targetMapField, matchMapper.srcMapField, matchMapper.targetValueField, matchMapper.srcValueField));
    }

    public static <T> List<T> merge(Collection<T>... colls){
        List<T> result = new ArrayList<>();
        for (Collection<T> coll : colls) {
            result.addAll(coll);
        }
        return result;
    }

    public static <T> Set<T> mergeBatDistinct(Collection<T>... colls){
        Set<T> result = new HashSet<>();
        for (Collection<T> coll : colls) {
            result.addAll(coll);
        }
        return result;
    }

    /**
     * 方法运行时间
     * @param supplier 回调函数
     * @return         方法运行时间
     */
    public static long runtime(RunTime supplier){
        long time = System.currentTimeMillis();
        supplier.run();
        long result = System.currentTimeMillis() - time;
        return result;
    }

    /**
     * 获取values
     * @param collection
     * @param supplier
     * @param fields
     * @param <T>
     * @return
     */
    private static <T extends Collection> Map<String, T> getValues2Collection(Collection<?> collection, Supplier<T> supplier, String... fields){
        Map<String, T> result = new HashMap<>(8);
        for(String fieid : fields){
            result.put(fieid, supplier.get());
        }
        if(collection.isEmpty()){
            return result;
        }
        collection.forEach(item -> {
            for(String fieid : fields){
                result.get(fieid).add(getValue(item, fieid));
            }
        });
        return result;
    }

    /**
     * 获取对象里属性值
     * @param o      对象
     * @param fields 字段名,支持级联
     * @return       属性值
     */
    private static Object getValueDeep(Object o, String[] fields, int index){
        if(o == null || index == fields.length){
            return o;
        }
        String field = fields[index];
        Matcher matcher = Const.PATTERN.matcher(field);
        int indexStart = field.indexOf("[");
        if(matcher.find() && indexStart != 0){
            String fidld1 = field.substring(0, indexStart);
            String indexField = field.substring(indexStart);
            Object value1 = getValue(o, fidld1);
            Object value2 = getValue(value1, indexField);
            return getValueDeep(value2, fields, index + 1);
        }else{
            Object value = getValue(o, field);
            return getValueDeep(value, fields, index + 1);
        }
    }

    /**
     * 获取get方法
     * @param clazz 类
     * @param field 属性名
     * @return      方法对象
     */
    private static Method getGetMethod(Class<?> clazz, String field){
        try {
            return clazz.getMethod("get" + field.toUpperCase().substring(0, 1) + field.substring(1));
        } catch (Exception e) {
            throw new ObjectUtilException(e);
        }
    }

    /**
     * 获取get方法
     * @param clazz 类
     * @param field 属性名
     * @return      方法对象
     */
    private static Method getSetMethod(Class<?> clazz, String field, Class<?> fieldClass){
        try {
            return clazz.getMethod("set" + field.toUpperCase().substring(0, 1) + field.substring(1), fieldClass);
        } catch (Exception e) {
            throw new ObjectUtilException(e);
        }
    }

}



